package com.sniper.web.shiro.session.redis;

import com.sniper.utils.DataUtil;
import com.sniper.utils.StringUtil;
import com.sniper.web.redis.RedisConnUtil;
import com.sniper.web.shiro.session.cache.CacheFactory;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.util.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;

import java.util.*;

/**
 * Redis缓存
 *
 * @param <K>
 * @param <V>
 * @author suzhen
 */
public class RedisCache<K, V> implements CacheFactory<K, V> {

    private static final Logger logger = LoggerFactory.getLogger(RedisCache.class);
    /**
     * The wrapped Jedis instance.
     */
    private RedisConnUtil cache;
    private long ttlTime = 1800000l;

    /**
     * The Redis key prefix for the sessions
     */
    private String keyPrefix = "shiro_redis_:";

    /**
     * Returns the Redis session keys prefix.
     *
     * @return The prefix
     */
    public String getKeyPrefix() {
        return keyPrefix;
    }

    /**
     * Sets the Redis sessions key prefix.
     *
     * @param keyPrefix The prefix
     */
    public void setKeyPrefix(String keyPrefix) {
        this.keyPrefix = keyPrefix;
    }

    /**
     * 通过一个JedisManager实例构造RedisCache
     */
    public RedisCache() {

    }

    public RedisCache(RedisConnUtil cache, long ttlTime, String keyPrefix) {
        this.cache = cache;
        this.ttlTime = ttlTime;
        this.keyPrefix = keyPrefix;
    }

    /**
     * 获得byte[]型的key
     *
     * @param key
     * @return
     */
    private byte[] getByteKey(K key) {
        if (key instanceof String) {
            String preKey = this.keyPrefix + key;
            return preKey.getBytes();
        } else {
            return DataUtil.byteMerger(keyPrefix.getBytes(), StringUtil.serialize(key));
        }
    }

    @Override
    public V get(K key) throws CacheException {

        Jedis jedis = cache.getJedis();
        try {
            if (key == null) {
                return null;
            } else {
                byte[] _k = getByteKey(key);
                byte[] rawValue = jedis.get(_k);
                V v = null;
                if (rawValue != null) {
                    v = (V) StringUtil.deserialize(rawValue);
                }
                logger.debug("根据key从Redis中获取对象 [" + key + "]");
                return v;
            }
        } catch (Throwable t) {
            //throw new CacheException(t);

        } finally {
            cache.close(jedis);
        }
        return null;
    }

    @Override
    public V put(K key, V value) throws CacheException {

        Jedis jedis = cache.getJedis();
        try {
            jedis.set(getByteKey(key), StringUtil.serialize(value));
            jedis.pexpire(getByteKey(key), ttlTime);
            logger.debug("根据key从存储 [" + key + "]");
            return value;
        } catch (Throwable t) {
            throw new CacheException(t);
        } finally {
            cache.close(jedis);
        }
    }

    @Override
    public V remove(K key) throws CacheException {
        Jedis jedis = cache.getJedis();
        try {
            V previous = get(key);
            jedis.del(getByteKey(key));
            logger.debug("从redis中删除 key [" + key + "]");
            return previous;
        } catch (Throwable t) {
            throw new CacheException(t);
        } finally {
            cache.close(jedis);
        }
    }

    @Override
    public void clear() throws CacheException {
        Jedis jedis = cache.getJedis();
        try {
            jedis.flushDB();
            logger.debug("从redis中删除所有元素");
        } catch (Throwable t) {
            throw new CacheException(t);
        } finally {
            cache.close(jedis);
        }
    }

    @Override
    public int size() {
        Jedis jedis = cache.getJedis();
        try {
            Long longSize = jedis.dbSize();
            return longSize.intValue();
        } catch (Throwable t) {
            throw new CacheException(t);
        } finally {
            cache.close(jedis);
        }
    }

    @Override
    public Set<K> keys() {
        Jedis jedis = cache.getJedis();
        try {
            Set<byte[]> keys = jedis.keys((this.keyPrefix + "*").getBytes());
            if (CollectionUtils.isEmpty(keys)) {
                return Collections.emptySet();
            } else {
                Set<K> newKeys = new HashSet<>();
                for (byte[] key : keys) {
                    newKeys.add((K) key);
                }
                return newKeys;
            }
        } catch (Throwable t) {
            throw new CacheException(t);
        } finally {
            cache.close(jedis);
        }
    }

    @Override
    public Collection<V> values() {
        Jedis jedis = cache.getJedis();
        try {
            Set<byte[]> keys = jedis.keys((this.keyPrefix + "*").getBytes());
            if (!CollectionUtils.isEmpty(keys)) {
                List<V> values = new ArrayList<V>(keys.size());
                for (byte[] key : keys) {
                    V value = get((K) key);
                    if (value != null) {
                        values.add(value);
                    }
                }
                return Collections.unmodifiableList(values);
            } else {
                return Collections.emptyList();
            }
        } catch (Throwable t) {
            throw new CacheException(t);
        } finally {
            cache.close(jedis);
        }
    }


    @Override
    public void put(K k, V v, long time) throws CacheException {
        Jedis jedis = cache.getJedis();
        try {
            jedis.set(getByteKey(k), StringUtil.serialize(v));
            jedis.pexpire(getByteKey(k), time);
            logger.debug("根据key从存储 [" + k + "]");
        } catch (Throwable t) {
            throw new CacheException(t);
        } finally {
            cache.close(jedis);
        }
    }


}
